﻿using OpenTK;
using OpenTK.Graphics.OpenGL;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;

namespace LDLib
{
    public class BitmapCharacter
    {
        public int index = -1;
        public int x = 0;
        public int y = 0;
        public int width = 0;
        public int height = 0;
        public int xOffset = 0;
        public int yOffset = 0;
        public int xAdvance = 0;
        public Dictionary<int, int> kernings = new Dictionary<int, int>();

        public object Clone()
        {
            BitmapCharacter result = new BitmapCharacter();
            result.x = x;
            result.y = y;
            result.width = width;
            result.height = height;
            result.xOffset = xOffset;
            result.yOffset = yOffset;
            result.xAdvance = xAdvance;
            // result.kerningList.AddRange(kerningList);

            return result;
        }
    }

    public class BitmapFont
    {
        public string file = "";
        public string filename = "";
        public string path = "";
        public string face = "";
        public int size = 32;
        public int lineHeight = 12;
        public int _base = 0;
        public int width = 1;
        public int height = 1;
        public int texture;
        public Dictionary<int, BitmapCharacter> chars = new Dictionary<int, BitmapCharacter>();
        public Dictionary<int, Mesh> meshes = new Dictionary<int, Mesh>();

        /// <summary>
        /// Constructs a new bitmap font from a file
        /// </summary>
        /// <param name="file">Bitmap font file</param>
        public BitmapFont(string file)
        {
            this.file = file;
            this.path = Path.GetDirectoryName(file);
            string[] lines = File.ReadAllLines(file);
            Parse(lines);
            Setup();
        }

        /// <summary>
        /// Constructs a new bitmap font from a string array representing
        /// the font information
        /// </summary>
        /// <param name="lines">Font representation</param>
        public BitmapFont(string[] lines)
        {
            this.file = null;
            Parse(lines);
            Setup();
        }

        /// <summary>
        /// Sets up the bitmap font
        /// </summary>
        private void Setup()
        {
            foreach (KeyValuePair<int, BitmapCharacter> pair in chars)
            {
                Mesh mesh = new Mesh();

                float uvx0 = (float)pair.Value.x / (float)width;
                float uvx1 = ((float)pair.Value.x + (float)pair.Value.width) / (float)width;
                float uvy0 = (float)pair.Value.y / (float)height;
                float uvy1 = ((float)pair.Value.y + (float)pair.Value.height) / (float)height;

                // float ratio = (float)pair.Value.width / (float)pair.Value.height;
                float w = (float)pair.Value.width / (float)width;
                float h = (float)pair.Value.height / (float)height;

                Vertex v0 = new Vertex(new Vector3(-w / 2f, -h, 0f), Vector3.UnitZ, new Vector2(uvx0, uvy1));
                Vertex v1 = new Vertex(new Vector3(w / 2f, -h, 0f), Vector3.UnitZ, new Vector2(uvx1, uvy1));
                Vertex v2 = new Vertex(new Vector3(-w / 2f, h, 0f), Vector3.UnitZ, new Vector2(uvx0, uvy0));
                Vertex v3 = new Vertex(new Vector3(w / 2f, h, 0f), Vector3.UnitZ, new Vector2(uvx1, uvy0));
                mesh.Vertices.Add(v0);
                mesh.Vertices.Add(v1);
                mesh.Vertices.Add(v2);
                mesh.Vertices.Add(v3);
                mesh.Indices.Add(0);
                mesh.Indices.Add(1);
                mesh.Indices.Add(2);
                mesh.Indices.Add(2);
                mesh.Indices.Add(1);
                mesh.Indices.Add(3);
                meshes.Add(pair.Key, mesh);
            }
        }

        /// <summary>
        /// Parses a bitmap font from an array of strings
        /// </summary>
        /// <param name="lines">Array of strings from the font file</param>
        private void Parse(string[] lines)
        {
            char[] splitters = new char[] { ' ', '=' };
            foreach (string s in lines)
            {
                string[] t = s.Split(splitters);
                switch (t[0])
                {
                    case "info":
                        for (int i = 1; i < t.Length; ++i)
                        {
                            if (t[i] == "size") size = int.Parse(t[i + 1]);
                            else if (t[i] == "face") face = t[i + 1];
                        }
                        break;
                    case "common":
                        for (int i = 1; i < t.Length; ++i)
                        {
                            if (t[i] == "lineHeight")
                            {
                                lineHeight = int.Parse(t[i + 1]);
                            }
                            else if (t[i] == "base")
                            {
                                _base = int.Parse(t[i + 1]);
                            }
                            else if (t[i] == "scaleW")
                            {
                                width = int.Parse(t[i + 1]);
                            }
                            else if (t[i] == "scaleH")
                            {
                                height = int.Parse(t[i + 1]);
                            }
                        }
                        break;
                    case "page":
                        for (int i = 1; i < t.Length; ++i)
                        {
                            if (t[i] == "file")
                            {
                                string textureFileName = t[i + 1];
                                textureFileName = textureFileName.Replace("\"", "");
                                textureFileName = textureFileName.Replace("\r", "");

                                if (file != null)
                                {
                                    texture = GL.GenTexture();
                                    Bitmap bitmap = new Bitmap(path + "\\" + textureFileName);
                                    BitmapData bd = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                                    GL.BindTexture(TextureTarget.Texture2D, texture);
                                    GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
                                    GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
                                    GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge);
                                    GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);
                                    GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, bitmap.Width, bitmap.Height, 0, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, bd.Scan0);
                                    GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
                                    GL.BindTexture(TextureTarget.Texture2D, 0);
                                    bitmap.UnlockBits(bd);
                                    bitmap.Dispose();
                                }
                                else
                                {
                                    texture = AssetLoader.LoadTexture(textureFileName, textureFileName);
                                }
                            }
                        }
                        break;
                    case "char":
                        BitmapCharacter bmChar = new BitmapCharacter();
                        for (int i = 1; i < t.Length; ++i)
                        {
                            if (t[i] == "id") bmChar.index = int.Parse(t[i + 1]);
                            else if (t[i] == "x") bmChar.x = int.Parse(t[i + 1]);
                            else if (t[i] == "y") bmChar.y = int.Parse(t[i + 1]);
                            else if (t[i] == "width") bmChar.width = int.Parse(t[i + 1]);
                            else if (t[i] == "height") bmChar.height = int.Parse(t[i + 1]);
                            else if (t[i] == "xoffset") bmChar.xOffset = int.Parse(t[i + 1]);
                            else if (t[i] == "yoffset") bmChar.yOffset = int.Parse(t[i + 1]);
                            else if (t[i] == "xadvance") bmChar.xAdvance = int.Parse(t[i + 1]);
                        }
                        chars.Add(bmChar.index, bmChar);
                        break;
                    case "kerning":
                        int index = 0;
                        // Kerning k = new Kerning();
                        int second = 0, amount = 0;
                        for (int i = 1; i < t.Length; ++i)
                        {
                            if (t[i] == "first") index = int.Parse(t[i + 1]);
                            if (t[i] == "second") second = int.Parse(t[i + 1]);
                            if (t[i] == "amount") amount = int.Parse(t[i + 1]);
                        }
                        chars[index].kernings.Add(second, amount);
                        // chars[index].kerningList.Add(k);
                        break;
                }
            }
        }
    }
}
